package com.sunxboy.platform.log4j;

import java.util.concurrent.TimeUnit;

import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.spi.LoggingEvent;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;

/**
 * Text email appender, with basic throttling algorithm to suppress message floods
 */
public class TextEmailAppender extends AppenderSkeleton implements Appender {

    private JavaMailSender mailSender;
    private SimpleMailMessage template;

    private long lastEmailSent = 0;
    private int emailsSent = 0;
    private int emailsMissed = 0;

    /**
     * Period in which to count generated emails/as well as cool off period
     */
    private long period = TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS);

    /**
     * Maximum allowed emails before we start discarding emails
     */
    private int maxEmailsPerPeriod = 5;
    private final boolean enableEmailSend;

    public TextEmailAppender(final JavaMailSender mailSender, final SimpleMailMessage template, final boolean enableEmail) {
        this.mailSender = mailSender;
        this.template = template;
        this.enableEmailSend = enableEmail;
    }

    public long getPeriod() {
        return period;
    }

    public void setPeriod(long throttlePeriod) {
        this.period = throttlePeriod;
    }

    public int getMaxEmailsPerPeriod() {
        return maxEmailsPerPeriod;
    }

    public void setMaxEmailsPerPeriod(int maxEmailPerPeriod) {
        this.maxEmailsPerPeriod = maxEmailPerPeriod;
    }

    @Override
    protected void append(LoggingEvent event) {
        if (!enableEmailSend) {
            return;
        }

        if (mailSender == null || template == null) {
            return;
        }

        if ((System.currentTimeMillis() - lastEmailSent) > period) {
            lastEmailSent = System.currentTimeMillis();
            emailsSent = 0;
        }

        emailsSent++;

        if (emailsSent <= maxEmailsPerPeriod) {
            String extraMessage = null;
            String subjectSuffix = null;

            if (emailsSent == maxEmailsPerPeriod) {
                lastEmailSent = System.currentTimeMillis();

                long suspendedFor = TimeUnit.SECONDS.convert(period, TimeUnit.MILLISECONDS);

                extraMessage = String.format("Email flood detected, new events will be discarded for %d seconds\n\n", suspendedFor);
                subjectSuffix = String.format("[M:ALERTS SUSPENDED FOR %d SECONDS]", suspendedFor);
            }
            else if (emailsMissed > 0) {
                extraMessage = String.format("Flood suppression logic discarded %d previous events\n\n", emailsMissed);
                subjectSuffix = String.format("[M:IGNORED %d EMAILS]", emailsMissed);
                emailsMissed = 0;
            }

            emailEvent(event, extraMessage, subjectSuffix);
            return;
        }
        else {
            emailsMissed++;
        }

    }

    private void emailEvent(LoggingEvent event, String extraMessage, String subjectSuffix) {
        final String subject;
        {
            StringBuilder sb = new StringBuilder(template.getSubject());
            sb.append("[L:").append(event.getLevel()).append("]");

            if (subjectSuffix != null) {
                sb.append(subjectSuffix);
            }

            subject = sb.toString();
        }

        final Layout layout = super.getLayout();
        StringBuilder sb = new StringBuilder();

        if (extraMessage != null) {
            sb.append(extraMessage);
        }

        if (layout.getHeader() != null) {
            sb.append(layout.getHeader());
        }
        sb.append(layout.format(event));

        if (layout.ignoresThrowable()) {
            if (event.getThrowableStrRep() != null) {
                for (String text : event.getThrowableStrRep()) {
                    sb.append(text).append("\r\n");
                }
            }
        }

        emailText(sb.toString(), subject);
    }

    private void emailText(String text, String subject) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage(template);

        simpleMailMessage.setSubject(subject);
        simpleMailMessage.setText(text);

        mailSender.send(simpleMailMessage);
    }

    public void close() {
    }

    public boolean requiresLayout() {
        return true;
    }

}
